#pragma once
#include <iostream>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <functional>
#include <string>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "err.hpp"
#include "threadPool.hpp"
#include "log.hpp"

using namespace std;

namespace ns_server{

    //
    static const int backlog = 32;

    using func_t = function<string(const string &)>;

    class tcp_server;
    class ThreadData{
    public:
        ThreadData(int fd, const string &ip, const uint16_t &port, tcp_server *ts)
        : sock(fd), clientip(ip), clientport(port), current(ts){

        }

    public:
        int sock;
        string clientip;
        uint16_t clientport;
        tcp_server *current;
    };

    class tcp_server{
    public:
    tcp_server(func_t func, uint16_t port = 8080): _func(func), _port(port), _quit(1){

    }

    ~tcp_server(){

    }

    void init(){

        //对 SIGCHLD -- 阻塞 的信号进行忽略，推荐
        signal(SIGCHLD, SIG_IGN);
        //signal(SIGCHLD, handler); -- 用回调函数回收信号，不太推荐

        //1、创建tcp套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock<0){
            // cerr << "create socket error: " << strerror(errno) << endl;
            logMessage(FATAL, "create socket error: %d:%s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        cout << "create socket success: " << _listensock << endl;

        //2、绑定
        //2.1、构建栈上的sockaddr_in结构体local
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        //主机转网络，只有在绑定的时候才需要手动转化，接收和发送的时候函数内部包含了转化，不用手动转化
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        //2.2、将local绑定到OS内核，我们前面定义的是sockaddr_in，这里需要强转
        if(bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){
            // cerr<<"bind socket error: "<< strerror(errno) << endl;
            logMessage(FATAL, "bind socket error: %d:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        cout << "bind success." << endl;

        //这上面都和udp的流程一样，从此处开始不一样
        //--------------------------------------

        //3、进入监听状态
        if(listen(_listensock,backlog)<0){
            // cerr<<"listen socket error: "<< strerror(errno) << endl;
            logMessage(FATAL, "listen socket error: %d:%s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }


    }

    void service(int sock, const string& clientip, const uint16_t& clientport){
        char buf[1024];
        string client = clientip + '-' + to_string(clientport);
        // while (1)
        // {
        //     //当文件读取
        //     ssize_t s = read(sock, buf, sizeof(buf) - 1);
        //     if(s>0){
        //         buf[s] = 0;
        //         //进行回调，交给上层处理
        //         string res = _func(buf);
        //         cout << client << "# " <<res << endl;
        //         write(sock, res.c_str(), res.size());
        //     }
        //     //客户端关闭了连接
        //     else if(s==0){
        //         close(sock);
        //         cout << client <<" quit, sock of this client close." << endl;
        //         break;
        //     }else{
        //         cerr << "read error: " << strerror(errno) << endl;
        //         break;
        //     }
        // }

        //每个用户每次发送消息的时候再请求链接，发送完就断开连接退出
        ssize_t s = read(sock, buf, sizeof(buf) - 1);
        if(s>0){
            buf[s] = 0;
            //进行回调，交给上层处理
            string res = _func(buf);
            cout << client << "# " <<res << endl;
            logMessage(DEBUG, "%s# %s", client.c_str(), res.c_str());
            write(sock, res.c_str(), res.size());
        }
        //客户端关闭了连接
        else if(s==0){
            close(sock);
            // cout << client <<" quit, sock of this client close." << endl;
            logMessage(INFO, "%s quit, sock of this client close.", client.c_str());
        }
        else{
            // cerr << "read error: " << strerror(errno) << endl;
            logMessage(ERROR, "read error: %d:%s", errno, strerror(errno));
        }

    }

    static void* threadRoutine(void *args){

        //线程分离
        pthread_detach(pthread_self());

        ThreadData *td = static_cast<ThreadData *>(args);
        td->current->service(td->sock, td->clientip, td->clientport);

        delete td;
    }

    void start(){
        //服务器不quit了
        _quit = false;
        while (!_quit){
            cout << "while(!quit)"<<endl;
            //4、获取连接，accept
            //被获取上来的套接字才是真正用来给客户端提供服务的
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sock = accept(_listensock, (struct sockaddr *)&client, &len);
            //提取客户端信息
            string clientip = inet_ntoa(client.sin_addr);
            uint16_t clientport = ntohs(client.sin_port);

            //获取失败转头获取下一个连接
            if(sock<0){
                cerr << "accept error " << endl;
                logMessage(WARNING, "accept error: %d:%s", errno, strerror(errno));
                sleep(1);
                continue;
            }
            // cout << "connect to " << sock << " from " << _listensock << ", " << clientip << "-" << clientport << endl;
            logMessage(INFO, "connect to %d from %d, %s-%d", sock, _listensock, clientip.c_str(), clientport);

            // //5、提供服务，业务处理
            // //这样写同时只有一个用户能使用服务器 -- 进到server之后进入循环，只有当前用户退出之后才退出循环，才退出server，才能再次accept
            // //需要进行多进程/多线程版本
            // service(sock, clientip, clientport);

            // //多线程
            // //多线程不需要关闭不要的fd -- 多线程的文件描述符是共享的
            // //线程回收join()的时候会阻塞，用线程分离detach解决，分离了就不用join了
            // pthread_t tid;
            // ThreadData *td = new ThreadData(sock, clientip, clientport, this);
            // pthread_create(&tid, nullptr, threadRoutine, td);

            //多线程和多进程的问题：用户来了才创建线程，慢，用线程池解决
            //构建任务
            //将server绑定到t中，让t调用server
            //由于server是类内函数，还要多传一个this在第一个位置，并且设置三个占位符分别占server的其他三个参数sock, clientip, clientport
            Task t(sock, clientip, clientport, std::bind(&tcp_server::service, this, placeholders::_1, placeholders::_2,placeholders::_3));
            //调用getInstance获取单例对象，再调用单例对象中的pushTask
            ThreadPool<Task>::getInstance()->pushTask(t);

            //线程池的问题：线程池中的线程数量应该是有限的，且不应该处理长任务，如果多个线程都要同时使用同一种业务逻辑，会造成资源抢占，降低运行速度
        }
    }

    private:
        uint16_t _port;
        //监听套接字的文件描述符，用于获取与客户端的连接，不断获取连接
        int _listensock;
        //服务器是否退出，默认为1，没有启动
        bool _quit;

        func_t _func;
    };
}